#pragma once

#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string>
#include <cstring>
#include "util.hpp"

const int READBUFS_SIZE = 1024;

namespace ns_email
{
    class CSendEmail
    {
    public:
        CSendEmail(
            int port,
            std::string smtpServer);
        ~CSendEmail();

        void SetFromEmail(std::string fromEmail);
        void SetToEmail(std::string toEmail);
        void SetPassward(std::string passward);
        void SetTitle(std::string title);
        void SetContent(std::string content);

        bool SendEmail(std::string toEmail, std::string title, std::string content);
        bool SendEmail();

    private:
        int m_port;
        std::string m_smtpServer;
        std::string m_fromEmail;
        std::string m_toEmail;
        std::string m_passwd;
        std::string m_title;
        std::string m_content;

        int m_socket;
        char readBuff[READBUFS_SIZE];

        bool ConnEmail();
        bool Read();
        bool Send(std::string msg);
        bool SendText();
        bool SendText(std::string toEmail, std::string title, std::string content);
        bool SendHead();
        bool SendEnd();
    };
    inline CSendEmail::CSendEmail(int port, std::string smtpServer)
    {
        m_port = port;
        m_smtpServer = smtpServer;
    }

    inline CSendEmail::~CSendEmail()
    {
        close(m_socket);
    }

    inline void CSendEmail::SetFromEmail(std::string fromEmail)
    {
        m_fromEmail = fromEmail;
    }

    inline void CSendEmail::SetToEmail(std::string toEmail)
    {
        m_toEmail = toEmail;
    }

    inline void CSendEmail::SetPassward(std::string passward)
    {
        m_passwd = passward;
    }

    inline void CSendEmail::SetTitle(std::string title)
    {
        m_title = title;
    }

    inline void CSendEmail::SetContent(std::string content)
    {
        m_content = content;
    }

    inline bool CSendEmail::SendEmail(std::string toEmail, std::string title, std::string content)
    {
        if (ConnEmail())
            return false;

        if (false == SendHead())
        {
            return false;
        }

        if (false == SendText(toEmail, title, content))
        {
            return false;
        }

        if (false == SendEnd())
        {
            return false;
        }

        return true;
    }

    inline bool CSendEmail::SendEmail()
    {

        if (false == ConnEmail())
            return false;

        if (false == SendHead())
        {
            return false;
        }

        if (false == SendText())
        {
            return false;
        }

        if (false == SendEnd())
        {
            return false;
        }

        return true;
    }

    inline bool CSendEmail::ConnEmail()
    {
        m_socket = socket(AF_INET, SOCK_STREAM, 0);
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(m_port);
        local.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr *)gethostbyname(m_smtpServer.c_str())->h_addr_list[0]));
        if (0 != connect(m_socket, (struct sockaddr *)&local, sizeof(local)))
        {

            return false;
        }
        return true;
    }

    inline bool CSendEmail::Read()
    {
        memset(readBuff, '\0', READBUFS_SIZE);
        int res = recv(m_socket, readBuff, READBUFS_SIZE, 0);
        std::cout << "READ: " << readBuff << std::endl;
        if (-1 == res)
        {
            return false;
        }
        return true;
    }

    inline bool CSendEmail::Send(std::string msg)
    {
        msg += "\r\n";
        int res = send(m_socket, msg.c_str(), msg.length(), 0);
        if (-1 == res)
        {
            return false;
        }
        return true;
    }

    inline bool CSendEmail::SendText()
    {
        if (false == Send("MAIL FROM:<" + m_fromEmail + ">") || false == Read())
        {
            return false;
        }

        if (false == Send("RCPT TO:<" + m_toEmail + ">") || false == Read())
        {
            return false;
        }

        if (false == Send("DATA") || false == Read())
        {
            return false;
        }
        std::string SendBuff;
        const char *ch = strchr(m_fromEmail.c_str(), '@');
        std::string name = std::string(m_fromEmail.c_str(), ch - m_fromEmail.c_str() - 1);

        SendBuff = "From:" + name + "<" + m_fromEmail.c_str() + ">\r\n";
        SendBuff += "Subject:" + m_title + "\r\n\r\n";
        SendBuff += m_content + "\r\n\r\n";
        SendBuff += "\r\n."; // 原本应该是"\r\n.\r\n", 由于使用Send进行发送，这个函数默认会在结尾添加一个\r\n

        return Send(SendBuff);
    }

    inline bool CSendEmail::SendText(std::string toEmail, std::string title, std::string content)
    {
        if (false == Send("MAIL FROM:<" + m_fromEmail + ">") || false == Read())
        {
            return false;
        }

        if (false == Send("RCPT TO:<" + toEmail + ">") || false == Read())
        {
            return false;
        }

        if (false == Send("DATA") || false == Read())
        {
            return false;
        }
        std::string SendBuff;
        const char *ch = strchr(m_fromEmail.c_str(), '@');
        std::string name = std::string(m_fromEmail.c_str(), ch - m_fromEmail.c_str() - 1);

        SendBuff = "From:" + name + "<" + m_fromEmail.c_str() + ">\r\n";
        SendBuff += "Subject:" + title + "\r\n\r\n";
        SendBuff += content + "\r\n\r\n";
        SendBuff += "\r\n."; // 原本应该是"\r\n.\r\n", 由于使用Send进行发送，这个函数默认会在结尾添加一个\r\n

        return Send(SendBuff);
    }

    inline bool CSendEmail::SendHead()
    {
        // 发送HELO MSG信息
        if (false == Send("HELO MSG") || false == Read())
        {
            return false;
        }

        if (false == Send("AUTH LOGIN") || false == Read())
        {
            return false;
        }

        std::string fromEmail_base64 = "";
        ns_util::Openssl::Base64Code(m_fromEmail, fromEmail_base64);
        if (false == Send(fromEmail_base64) || false == Read())
        {
            return false;
        }

        std::string passwd_base64 = "";
        ns_util::Openssl::Base64Code(m_passwd, passwd_base64);
        if (false == Send(passwd_base64) || false == Read())
        {
            return false;
        }
        return true;
    }

    inline bool CSendEmail::SendEnd()
    {
        if (false == Send("Quit") || false == Read())
        {
            return false;
        }
        return true;
    }
}